﻿using System;
using System.Collections.Generic;
using System.Linq;
using MyBasicLib.Collections.Generic;

namespace MyBasicLib.Extensions
{
    public static class ITreeNodeExtension
    {
        [Obsolete]
        public static  T GetRootNode <T>(this IEnumerable<T> list) where T : ITreeNode
        { return GetRootNodeList(list).FirstOrDefault(); }
        public static IEnumerable<T> GetRootNodeList<T>(this IEnumerable<T> list) where T : ITreeNode
        {
            Check.Argument.IsNotNull(list, "list");
            return list.Where(l => !list.Any(a => a.Id == l.ParentId));
            //return list.SingleOrDefault(p => p.ParentId == Config.Collection.ParentIdOfRootNode);
        }

        public static T GetNodeById<T>(this IEnumerable<T> list, string id) where T : ITreeNode
        {
            Check.Argument.IsNotNull(list, "list");
            Check.Argument.IsNotEmpty(id, "id");
            return list.SingleOrDefault(p => p.Id == id);
        }

        public static IEnumerable<T> GetNodesByParentId<T>(this IEnumerable<T> list, string id) where T : ITreeNode
        {
            Check.Argument.IsNotNull(list, "list");
            return list.Where(p => p.ParentId == id);
        }

        public static T GetParentNode<T>(this IEnumerable<T> list, string id) where T : ITreeNode
        {
            Check.Argument.IsNotNull(list, "list");
            T org = list.GetNodeById(id);
            if (org == null || string.IsNullOrEmpty(org.ParentId))
                return default(T);
            return list.GetNodeById(org.ParentId);
        }

        /// <summary>
        /// 获取节点深度，从0开始
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static int GetNodeDeep<T>(this IEnumerable<T> list, string id) where T : ITreeNode
        {
            Check.Argument.IsNotNull(list, "list");
            Check.Argument.IsNotEmpty(id, "id");
            // list.GetAllParentNodes(id).Count();
            T dep = GetNodeById(list, id);
            if (dep == null)
                return -1;
            if (string.IsNullOrEmpty(dep.ParentId))
                return 0;
            return list.GetNodeDeep(dep.ParentId) + 1;
        }

        /// <summary>
        /// get all child node recursively 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="id"></param>
        /// <param name="includeSelf"></param>
        /// <returns></returns>
        public static IEnumerable<T> GetAllChildNodes<T>(this IEnumerable<T> list, string id, bool includeSelf=false) where T : ITreeNode
        {
            Check.Argument.IsNotNull(list, "list");
            Check.Argument.IsNotEmpty(id, "id");
            if (id == null) return new T[0];
            var listOrg = new List<T>();
            var stack = new Stack<T>();
            T root = list.GetNodeById(id);
            if (root == null) return new T[0];
            stack.Push(root);
            while (stack.Count != 0)
            {
                T org = stack.Pop();
                if (includeSelf || org.Id != id)
                    listOrg.Add(org);
                IEnumerable<T> child = list.GetNodesByParentId(org.Id);
                foreach (T c in child)
                {
                    stack.Push(c);
                }
            }
            return listOrg;
        }

        /// <summary>
        /// get all parent node recursively 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="id"></param>
        /// <param name="includeSelf"></param> 
        /// <returns></returns>
        public static IEnumerable<T> GetAllParentNodes<T>(this IEnumerable<T> list, string id, bool includeSelf = false) where T : ITreeNode
        {
            Check.Argument.IsNotNull(list, "list");
            Check.Argument.IsNotEmpty(id, "id");
            if (id == null) return new T[0];
            T root = list.GetNodeById(id);
            if (root == null) return new T[0];
            var listOrg = new List<T>();
            var stack = new Stack<T>();
            stack.Push(root);
            if (includeSelf) listOrg.Add(root);
            while (stack.Count != 0)
            {
                T org = stack.Pop();
                T child = list.GetParentNode(org.Id);
                if (child != null)
                {
                    stack.Push(child);
                    listOrg.Add(child);
                }
            }
            return listOrg;
        }

        static void VisitPreorder<T>(this IEnumerable<T> list, string rootId, Action<T> act) where T : ITreeNode
        {
            T root = list.GetNodeById(rootId);
            if (null == root) return;
            act(root);

            foreach (T node in list.GetNodesByParentId(rootId))
            {
                VisitPreorder(list, node.Id, act);
            }
        }

        //static void VisitChild<T>(this IEnumerable<T> list, string parentId, Action<T> act) where T : ITreeNode
        //{  
        //    foreach (var node in list.GetNodesByParentId(parentId)) {
        //        act(node);
        //        VisitChild(list, node.Id, act);
        //}
    }
}